﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using AmiEmuLib.Host.HostInput;
using AmiEmuLib.Properties;
using log4net;
using System.Globalization;
using TomanuExtensions;

namespace AmiEmuLib.Host
{
    public delegate void MessageEvent(ref Message a_msg);
    public delegate void InputLanguageChangedEvent(byte a_char_set, 
        CultureInfo a_culture, InputLanguage a_input_language);

    /// <summary>
    /// Connection between main window (handle, messages) and AmiEmuLib.
    /// </summary>
    public static class HostEnvironment
    {
        /// <summary>
        /// Logger.
        /// </summary>
        private static readonly ILog Logger =
            LogManager.GetLogger(typeof(HostEnvironment).GetParentFullName());

        /// <summary>
        /// Connection to host window.
        /// </summary>
        private static HostWindow m_host_window;

        /// <summary>
        /// Place where we keep our settings.
        /// </summary>
        public static readonly string SettingsDir =
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
            Path.DirectorySeparatorChar + Settings.Default.ProgramName + Path.DirectorySeparatorChar;

        /// <summary>
        /// Connect to main window.
        /// </summary>
        /// <param name="a_host_window"></param>
        public static void AttachToHostWindow(HostWindow a_host_window)
        {
            if (m_host_window != null)
            {
                Logger.Error("Initialize called second time.");
                DetachFromHostWindow();
            }

            m_host_window = a_host_window;

            m_host_window.DeviceChanged += DoDeviceChanged;
            m_host_window.Input += DoInput;
            m_host_window.InputLanguageChanged += DoInputLanguageChanged;
            m_host_window.FormClosed += DoFormClosed;

            HostInputDevicesList.Connect();
        }

        /// <summary>
        /// Disconnect from main window.
        /// Note: when form is closing this method will be called automatically.
        /// </summary>
        public static void DetachFromHostWindow()
        {
            if (m_host_window == null)
            {
                Logger.Error("HostEnvironment is not attached to main window.");
                return;
            }

            m_host_window.DeviceChanged -= DoDeviceChanged;
            m_host_window.Input -= DoInput;
            m_host_window.InputLanguageChanged -= DoInputLanguageChanged;
            m_host_window.FormClosed -= DoFormClosed;

            if (HostWindowDetached != null)
                HostWindowDetached();

            m_host_window = null;
        }

        /// <summary>
        /// Quit. Do some final cleanup.
        /// </summary>
        public static event Action HostWindowDetached;

        /// <summary>
        /// Device changed.
        /// </summary>
        public static event MessageEvent Input;

        /// <summary>
        /// Input event.
        /// </summary>
        /// <param name="a_message"></param>
        /// <returns></returns>
        public static event Action DeviceChanged;

        /// <summary>
        /// Language or keyboard layout changed.
        /// </summary>
        public static event InputLanguageChangedEvent InputLanguageChanged;

        /// <summary>
        /// Main window handle, you shoudl set it at very begin.
        /// </summary>
        public static IntPtr WindowHandle
        {
            get
            {
                if (m_host_window == null)
                {
                    Logger.Error("Host window is not connected.");
                    return IntPtr.Zero;
                }

                return m_host_window.Invoke(() => m_host_window.Handle );
            }
        }

        /// <summary>
        /// Called from host window.
        /// </summary>
        private static void DoInputLanguageChanged(object a_sender, 
            InputLanguageChangedEventArgs a_args)
        {
            Logger.Info("DoInputLanguageChanged");
            if (InputLanguageChanged != null)
                InputLanguageChanged(a_args.CharSet, a_args.Culture, a_args.InputLanguage);
        }

        /// <summary>
        /// Called from host window.
        /// </summary>
        /// <param name="a_msg"></param>
        private static void DoDeviceChanged(ref Message a_msg)
        {
            Logger.InfoFormat("DoDeviceChanged, 0x{0:X8}", a_msg.WParam);
            if (DeviceChanged != null)
                DeviceChanged();
        }

        /// <summary>
        /// Called from host window.
        /// </summary>
        /// <param name="a_msg"></param>
        private static void DoInput(ref Message a_msg)
        {
            if (Input != null)
                Input(ref a_msg);
        }

        /// <summary>
        /// Called from host window.
        /// </summary>
        /// <param name="a_sender"></param>
        /// <param name="a_args"></param>
        private static void DoFormClosed(object a_sender, FormClosedEventArgs a_args)
        {
            Logger.Info("DoFormClosed");
            DetachFromHostWindow();
        }
    }
}